嵌入式Linux开发

您所在的位置:网站首页 485 两根线 嵌入式Linux开发

嵌入式Linux开发

2024-07-13 09:46| 来源: 网络整理| 查看: 265

提醒:RS485是串口中的一种,它与常用的TTL-UART的使用方式基本相同,差别在于使用485时需要手动切换485芯片的收发引脚状态模式。Linux 驱动RS485通信的程序源码Demo见文末。

1、RS485基础铺垫

        智能仪表随着80年代初单片机技术的成熟而发展起来,世界仪表市场基本被智能仪表所垄断,这归结于企业信息化的需要,而企业在仪表选型时其中的一个必要条件就是要具有联网通信接口。最初是数据模拟信号输出简单过程量,后来仪表接口是RS-232接口,这种接口可以实现点对点的通信方式,但这种方式不能实现联网功能,随后出现的RS-485解决了这个问题。RS-485又名TIA-485-A, ANSI/TIA/EIA-485或TIA/EIA-485。

RS-485有两线制和四线制两种接线,四线制只能实现点对点的通信方式,现很少采用,多采用的是两线制接线方式,这种接线方式为总线式拓扑结构,在同一总线上最多可以挂接32个节点。

在RS-485通信网络中一般采用的是主从通信方式,即一个主机带多个从机。

RS-485能够进行远距离传输主要得益于使用差分信号进行传输,当有噪声干扰时仍可以使用线路上两者差值进行判断,使传输数据不受噪声干扰。

差分传输是一种信号传输的技术,区别于传统的一根信号线一根地线的做法,差分传输在这两根线上都传输信号,这两个信号的振幅相等,相位相反。在这两根线上传输的信号就是差分信号,也称差模信号。

差分信号又称差模信号,是相对共模信号而言的。

共模信号是信号线对地的电压,差模信号是信号线之间的电压。放大电路是一个双口网络,每个端口有两个端子。当两个输入端子的输入信号分别为U1和U2时,两信号的差值称为差模信号,而两信号的算术平均值称为共模信号。

RS-485差分线路包括以下2个信号:

A:非反向(non-inverting)信号B:反向(inverting)信号

为避免信号反射,当线缆长度很长时数据传输线必须有终点,并且分支长度尽可能的短。正确的终端需要终端电阻RT匹配,其值为传输线的特性阻抗Z0。

RS-485标准建议线缆的Z0=120Ω。线缆干线通常终端匹配120Ω的电阻,线缆的末尾处各一个。

此外,在RS485的基础上,还衍生了Modbus等通信标准。

2、RS485硬件说明

(如果看不懂可以跳过,确定RS485的控制GPIO是哪个就行)

Linux开发板的RS485部分的原理图

SP3485芯片内部逻辑电路图

通过开发板的RS485部分的原理图和SP3485的芯片内部逻辑图可知:

GPIO_RX485_CTL引脚为高电平时,RS485为发送模式,

GPIO_RX485_CTL引脚为低电平时,RS485为接收模式。

SP3485芯片原厂说明手册,建议多查阅厂商手册,对于理解各类技术文档资料和开发能力的提升有非常大的帮助。

3、RS485通信功能实现

实际上,使用RS485通信的本质还是在使用UART串口通信,只是在程序编写时,需要对read、write多一步的处理。即,进行写485操作时,通过控制引脚切换485芯片为发送模式,读操作时,通过控制引脚切换485芯片为接收模式。485处理芯片相对于串口,就是需要将TTL逻辑电平信号转换为RS485差分电平信号。

如果觉得在代码中切换485芯片操作不方便,甚至可以直接使用TTL串口转485等模块,以串口方式驱动硬件设备。

本文中使用的是RS485转USB模块,根据实际情况连接设备,不能接错线。

连接开发板和硬件设备的RS485引脚A、B(A线接A线,B线接B线)

查看开发板串口3对应的tty设备号及RS485控制端口引脚对应的编号

 ttymxc 是指 i.MX 系列芯片上的一个串口设备。其它Linux开发板根据板子实际情况选择。

编写RS485测试程序,交叉编译到Linux开发板中运行

开发板运行RS485程序,接收及发送数据运行效果图

4、Linux RS485通信程序源码

        main.c

#include #include #include #include "linux-rs485.h" int main(int argc, char **argv) { int fd_rs485; int ret; ret = rs485_init_for_linux(&fd_rs485, B115200); if(ret < 0) { return -1; } printf("RS485 test Starting...\n"); char buf[1024] = {0}; while(1) { rs485_set_gpio_value(RS485_CTRL_GPIO, RS485_SEND_MODE); write(fd_rs485,"hello rs485\r\n", strlen("hello rs485\r\n")); sleep(1); rs485_set_gpio_value(RS485_CTRL_GPIO, RS485_RECV_MODE); read(fd_rs485,buf,1024); printf("recv:%s\r\n", buf); memset(buf, 0, 1024); sleep(1); } return 0; }

        linux-rs485.c

#include "linux-rs485.h" /** * @brief linux开发板RS485通信初始化 * @param fd:打开RS485通信文件描述符 * @param baudrate:485通信波特率 * @retval 成功返回0,失败返回-1 */ int rs485_init_for_linux(int *fd, speed_t baudrate) { //0、打开串口 /dev/ttymxc2 *fd = open("/dev/ttymxc2", O_RDWR); //具体的tty设备号,需根据开发板的实际情况修改 if(*fd == -1) { perror("open /dev/ttymxc2 failed!"); return -1; } //1.定义串口结构体 struct termios old_cfg, new_cfg; //2.获取当前串口的属性配置 if(tcgetattr(*fd, &old_cfg) != 0) { perror("tcgetattr"); close(*fd); return -1; } //3.设置串口的通信模式 bzero(&new_cfg, sizeof(new_cfg)); new_cfg = old_cfg; cfmakeraw(&new_cfg); //4.配置串口信息 cfsetispeed(&new_cfg, baudrate); cfsetospeed(&new_cfg, baudrate); new_cfg.c_cflag |= CLOCAL | CREAD; //设置数据位:8位 new_cfg.c_cflag &= ~CSIZE; new_cfg.c_cflag |= CS8; //设置校验位 new_cfg.c_cflag &= ~PARENB; //设置停止位 new_cfg.c_cflag &= ~CSTOPB; //清空缓冲区 new_cfg.c_cc[VTIME] = 0; new_cfg.c_cc[VMIN] = 4; tcflush(*fd, TCIFLUSH); //5.将配置信息同步到系统中 if((tcsetattr(*fd, TCSANOW, &new_cfg)) != 0) { perror("tcsetattr"); close(*fd); return -1; } //6、配置485通信控制IO rs485_set_gpio_export(RS485_CTRL_GPIO); rs485_set_gpio_direction(RS485_CTRL_GPIO, GPIO_OUT_MODE); return 0; } /** * @brief 设置RS485引脚export * @param gpio:端口引脚号 * @retval 成功返回0,失败返回-1 */ int rs485_set_gpio_export(unsigned int gpio) { int fd, len; char buf[128]; fd = open( "/sys/class/gpio/export", O_WRONLY); if (fd < 0) { perror("gpio/export"); return -1; } len = snprintf(buf, sizeof(buf), "%d", gpio);//从数字变换为字符串,即1 变为”1“ write(fd, buf, len);//将需要导出的GPIO引脚编号进行写入 close(fd); return 0; } /** * @brief 设置RS485引脚unexport * @param gpio:端口引脚号 * @retval 成功返回0,失败返回-1 */ int rs485_set_gpio_unexport(unsigned int gpio) { int fd, len; char buf[128]; fd = open("/sys/class/gpio/unexport", O_WRONLY); if (fd < 0) { perror("gpio/export"); return -1; } len = snprintf(buf, sizeof(buf), "%d", gpio);//从数字变换为字符串,即1 变为”1“ write(fd, buf, len);//将需要取消导出的GPIO引脚编号进行写入 close(fd); return 0; } /** * @brief 设置RS485引脚方向 * @param gpio:端口引脚号 * @param io_flag:引脚IO的状态 * @retval 成功返回0,失败返回-1 */ int rs485_set_gpio_direction(unsigned int gpio, unsigned int io_flag) { int fd, len; char buf[128]; len = snprintf(buf, sizeof(buf), "/sys/class/gpio" "/gpio%d/direction", gpio); fd = open(buf, O_WRONLY); if (fd < 0) { perror(buf); return -1; } if (io_flag)//为1,则写入“out",即设置为输出 write(fd, "out", 4); else//为0,则写入“in",即设置为输入 write(fd, "in", 3); close(fd); return 0; } /** * @brief 设置RS485引脚电平值 * @param gpio:端口引脚号 * @param value:电平值(高电平、低电平) * @retval 成功返回0,失败返回-1 */ int rs485_set_gpio_value(unsigned int gpio, unsigned int value) { int fd, len; char buf[128]; len = snprintf(buf, sizeof(buf), "/sys/class/gpio" "/gpio%d/value", gpio); fd = open(buf, O_WRONLY); if (fd < 0) { perror(buf); return -1; } if (value)//为1,则写入“1",即设置为输出高电平 write(fd, "1", 2); else//为0,则写入“0",即设置为输出低电平 write(fd, "0", 2); close(fd); return 0; } /** * @brief 设置RS485引脚方向 * @param gpio:端口引脚号 * @param value:获取的引脚值 * @retval 成功返回0,失败返回-1 */ int rs485_get_gpio_value(unsigned int gpio, unsigned int *value) { int fd, len; char buf[128]; char ch; len = snprintf(buf, sizeof(buf), "/sys/class/gpio" "/gpio%d/value", gpio); fd = open(buf, O_RDONLY); if (fd < 0) { perror("gpio/get-value"); return fd; } read(fd, &ch, 1);//读取外部输入电平 if (ch != '0') //为'1',则设置为1,即输入为高电平 { *value = 1; } else { //为'0',则设置为0,即输入为低电平 *value = 0; } close(fd); return 0; }

        rs485-linux.h

#ifndef __LINUX_RS485_H #define __LINUX_RS485_H #ifdef __cplusplus extern "C"{ #endif #include #include #include #include #include #include #include #include #define RS485_CTRL_GPIO 128 //RS485具体的控制引脚需根据开发板实际情况修改 #define GPIO_OUT_MODE 1 #define GPIO_IN_MODE 0 #define RS485_RECV_MODE 0 #define RS485_SEND_MODE 1 int rs485_init_for_linux(int *fd, speed_t baudrate); int rs485_set_gpio_export(unsigned int gpio); int rs485_set_gpio_unexport(unsigned int gpio); int rs485_set_gpio_direction(unsigned int gpio, unsigned int io_flag); int rs485_set_gpio_value(unsigned int gpio, unsigned int value); int rs485_get_gpio_value(unsigned int gpio, unsigned int *value); #ifdef __cplusplus } #endif #endif

嵌入式Linux开发---UART串口通信驱动硬件编程_嵌入式linux串口编程-CSDN博客

嵌入式Linux开发---Socket CAN通信驱动硬件编程_linux can源码-CSDN博客



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3